//    Openbravo POS is a point of sales application designed for touch screens.
//    Copyright (S) 2007-2009 Openbravo, S.L.
//    http://www.openbravo.com/product/pos
//
//    This file is part of Openbravo POS.
//
//    Openbravo POS is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    Openbravo POS is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with Openbravo POS.  If not, see <http://www.gnu.org/licenses/>.

package com.openbravo.data.loader;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

import com.openbravo.data.basic.BasicException;
import com.openbravo.data.loader.dialect.AbstractDBDialect;
import com.openbravo.data.loader.dialect.H2.H2DBDialect;
import com.openbravo.data.loader.dialect.PostgreSQL.PostgreSQLDBDialect;
import com.openbravo.data.loader.dialect.SessionDialect;
import com.openbravo.data.loader.dialect.SessionDBGeneric;
import com.openbravo.data.loader.dialect.Derby.DerbyDBDialect;
import com.openbravo.data.loader.dialect.HSQLDB.HSQLDBDialect;
import com.openbravo.data.loader.dialect.MySQL.MySQLDBDialect;
import com.openbravo.data.loader.dialect.Oracle.OracleDBDialect;
import com.openbravo.data.loader.dialect.SQLite.SQLiteDBDialect;
import e.odbo.data.OdbOContent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.sql.DataSource;

/**
 * @author adrianromero
 * Created on February 6, 2007, 4:06 PM
 */
public class DBSession implements I_Session {

    public static final Logger logger= LoggerFactory.getLogger(DBSession.class);

    private String m_surl;
    private String m_suser;
    private String m_spassword;

    private Connection m_c;
    private boolean m_bInTransaction;

    private final AbstractDBDialect dbDialect;
    protected DataSource dataSource;

    /**
     * Creates a new instance of DBSession
     */
    public DBSession(String url, String user, String password) throws BasicException {
        m_surl = url;
        m_suser = user;
        m_spassword = password;

        m_c = null;
        m_bInTransaction = false;
        connect(); // no lazy connection

        dbDialect = (AbstractDBDialect)getDiff();
    }

    public DBSession(DataSource dataSource)throws BasicException{
        this.dataSource=dataSource;
        m_c = null;
        m_bInTransaction = false;

        connect(); // no lazy connection

        dbDialect = (AbstractDBDialect)getDiff();
        close();
    }

    /* (non-Javadoc)
     * @see com.openbravo.data.loader.I_Session#connect()
     */
    @Override
    public void connect() throws BasicException {

        // primero cerramos si no estabamos cerrados
        close();
      try {
          if(dataSource!=null)
              m_c=dataSource.getConnection();
          else
             // creamos una nueva conexion.
             m_c = (m_suser == null && m_spassword == null)
                  ? DriverManager.getConnection(m_surl)
                  : DriverManager.getConnection(m_surl, m_suser, m_spassword);
          m_c.setAutoCommit(true);
          m_bInTransaction = false;
      }catch (SQLException e){
          throw new BasicException(e);
      }
    }

    /* (non-Javadoc)
     * @see com.openbravo.data.loader.I_Session#close()
     */
    @Override
    public void close() {

        if (m_c != null) {
            try {
                if (m_bInTransaction) {
                    m_bInTransaction = false; // lo primero salimos del estado
                    m_c.rollback();
                    m_c.setAutoCommit(true);
                }
                m_c.close();
            } catch (SQLException e) {
                // me la como
            } finally {
                m_c = null;
            }
        }
    }

    /* (non-Javadoc)
     * @see com.openbravo.data.loader.I_Session#getConnection()
     */
    @Override
    public Connection getConnection() throws BasicException {

        if (!m_bInTransaction) {
            ensureConnection();
        }
        return m_c;
    }

    /* (non-Javadoc)
     * @see com.openbravo.data.loader.I_Session#begin()
     */
    @Override
    public void begin() throws BasicException {

        if (m_bInTransaction) {
            throw new BasicException("Already in transaction");
        } else {
            try{
                ensureConnection();
                m_c.setAutoCommit(false);
                m_bInTransaction = true;
            }catch (SQLException e){
                throw new BasicException(e);
            }
        }
    }

    /* (non-Javadoc)
     * @see com.openbravo.data.loader.I_Session#commit()
     */
    @Override
    public void commit() throws BasicException {
        if (m_bInTransaction) {
            try{
            m_bInTransaction = false; // lo primero salimos del estado
            m_c.commit();
            m_c.setAutoCommit(true);
            }catch (SQLException e){
                throw new BasicException(e);
            }
        } else {
            throw new BasicException("Transaction not started");
        }
    }

    /* (non-Javadoc)
     * @see com.openbravo.data.loader.I_Session#rollback()
     */
    @Override
    public void rollback() throws BasicException {
        if (m_bInTransaction) {
            try{
               m_bInTransaction = false; // lo primero salimos del estado
               m_c.rollback();
               m_c.setAutoCommit(true);
            }catch (SQLException e){
                throw new BasicException(e);
            }
        } else {
            throw new BasicException("Transaction not started");
        }
    }

    /* (non-Javadoc)
     * @see com.openbravo.data.loader.I_Session#isTransaction()
     */
    @Override
    public boolean isTransaction() {
        return m_bInTransaction;
    }

    private void ensureConnection() throws BasicException {
        // solo se invoca si isTransaction == false

        boolean bclosed;
        try {
            bclosed = m_c == null || m_c.isClosed();
        } catch (SQLException e) {
            bclosed = true;
        }

        // reconnect if closed
        if (bclosed) {
            connect();
        }
    }


    /* (non-Javadoc)
     * @see com.openbravo.data.loader.I_Session#getURL()
     */
    @Override
    public String getURL() throws BasicException {
        String url="no sample";
        try{
            url=getConnection().getMetaData().getURL();
        }catch (SQLException e){
            throw new BasicException(e);
        }
        return url;
    }

    private SessionDialect getDiff() throws BasicException {
        String sdbmanager;
        try{
            sdbmanager = getConnection().getMetaData().getDatabaseProductName();
            logger.debug("this sql db type name:{}",sdbmanager);
        }catch (SQLException e){
            throw new BasicException(e);
        }
        if ("HSQL Database Engine".equals(sdbmanager)) {
            return new HSQLDBDialect();
        } else if ("MySQL".equals(sdbmanager)) {
            return new MySQLDBDialect();
        } else if ("PostgreSQL".equals(sdbmanager)) {
            return new PostgreSQLDBDialect();
        } else if ("Oracle".equals(sdbmanager)) {
            return new OracleDBDialect();
        } else if ("Apache Derby".equals(sdbmanager)) {
            return new DerbyDBDialect();
        } else if ("SQLite for Android".equals(sdbmanager)) {
            return new SQLiteDBDialect();
        } else if ("SQLite".equals(sdbmanager)) {
            return new SQLiteDBDialect();
        }else if ("H2".equals(sdbmanager)) {
            return new H2DBDialect();
        } else {
            return new SessionDBGeneric(sdbmanager);
        }
    }

    @Override
    public SessionDialect getDialect() {
        return dbDialect;
    }

    @Override
    public String getPlatform() {
        return OdbOContent.PLATFORM_DB;
    }

    @Override
    public Connection getConnection(boolean readOnly) throws BasicException {
        return getConnection();
    }
}
